#include "shimming.h"
#include "shimmingitem.h"
#include <cmath>

IndShimming::IndShimming(QWidget *parent)
    : QWidget{parent}
{
    setAttribute(Qt::WA_StyledBackground, true);
    m_nodeNumber.resize(MAX_CELL_COUNTS, 0);
    m_cellCounts.resize(MAX_CELL_COUNTS, 0);
    m_cog = new QLabel(this);
    m_cog->setFixedSize(COG_DIAMETER_RADIUS * 2, COG_DIAMETER_RADIUS * 2);
    m_cog->raise();
    QString cogStyleSheet = QString("background-color: %1; border-radius: %2")
                                .arg("#003868")
                                .arg(COG_DIAMETER_RADIUS);
    m_cog->setStyleSheet(cogStyleSheet);
    m_cog->hide();
}

void IndShimming::SetShowHollowPoint(bool flag)
{
    m_showHollowPoint = flag;
    update();
}

void IndShimming::SetShowCOG(bool flag)
{
    m_showCOG = flag;
    update();
}

void IndShimming::paintEvent(QPaintEvent* event)
{
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
    painter.translate(width() / 2, height() / 2);

    if (m_showHollowPoint)
    {
        DrawHollowPoint(painter);
    }
    DrawNodes(painter);
    DrawHR();
    if (m_showCOG)
    {
        m_cog->show();
        m_cog->raise();
        DrawCOG(painter);
    } else {
        m_cog->hide();
    }
}

void IndShimming::DrawHollowPoint(QPainter& painter)
{
    painter.save();

    QPen pen;
    pen.setWidth(1);
    pen.setColor(QColor(0xff003868));
    painter.setPen(pen);
    painter.drawEllipse(QPoint(0, 0), COG_DIAMETER_RADIUS, COG_DIAMETER_RADIUS);

    painter.restore();
}

void IndShimming::DrawCOG(QPainter& painter)
{
    painter.save();

    painter.setBrush(QColor(0xff003868));
    QPen pen;
    pen.setWidth(1);
    pen.setColor(Qt::transparent);
    painter.setPen(pen);

    painter.drawEllipse(QPointF(m_COG_x, -m_COG_y), COG_DIAMETER_RADIUS, COG_DIAMETER_RADIUS);

    painter.restore();
}

void IndShimming::DrawNodes(QPainter& painter)
{
    switch (m_items.size()) {
    case 3:
        DrawThreeNodes(painter);
        break;
    case 4:
        DrawFourNodes(painter);
        break;
    default:
        break;
    }
}

void IndShimming::DrawThreeNodes(QPainter& painter)
{
    QPoint top(width() / 2 - RETANGULAR_WIDTH / 2,
               height() / 2 - m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2);
    QPoint bottomRight(width() / 2 + m_polygonWidth / 2 - RETANGULAR_WIDTH / 2,
                       height() / 2 + m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2);
    QPoint bottomLeft(width() / 2 - m_polygonWidth / 2 - RETANGULAR_WIDTH / 2,
                      height() / 2 + m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2);
    if (m_items.size() == 3)
    {
        m_items[m_nodeNumber[0]]->move(top);
        m_items[m_nodeNumber[1]]->move(bottomRight);
        m_items[m_nodeNumber[2]]->move(bottomLeft);
    }
    if (m_lineStyle != LineStyle::HIDE)
    {
        double c = std::sqrt(std::pow(m_polygonWidth / 2, 2) + std::pow(m_polygonHeight, 2));
        double sin = m_polygonWidth / 2 / c;
        double cos = m_polygonHeight / c;
        double tan = sin / cos;
        DrawLine(painter, QPointF(RETANGULAR_HEIGHT / 2 * tan, -(m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2)), QPointF(RETANGULAR_WIDTH / 2 - RETANGULAR_HEIGHT / 2 * tan + m_polygonWidth / 2 - RETANGULAR_WIDTH / 2, m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2));
        DrawLine(painter, QPointF(m_polygonWidth / 2 - RETANGULAR_WIDTH / 2, m_polygonHeight / 2), QPointF(-(m_polygonWidth / 2 - RETANGULAR_WIDTH / 2), m_polygonHeight / 2));
    }

    painter.save();

    QFont font;
    font.setFamilies({"Noto Sans", "sans-serif"});
    font.setPixelSize(22);
    painter.setFont(font);
    painter.setPen(QColor("#003868"));

    QRect rect1(-(RETANGULAR_WIDTH / 2 + TEXT_WIDTH), -(m_polygonHeight / 2 + TEXT_WIDTH / 2), TEXT_WIDTH, TEXT_WIDTH);
    painter.drawText(rect1, Qt::AlignCenter, QString(QString::number(m_nodeNumber[0])));

    QRect rect2((m_polygonWidth / 2 + RETANGULAR_WIDTH / 2), (m_polygonHeight / 2 - TEXT_WIDTH / 2), TEXT_WIDTH, TEXT_WIDTH);
    painter.drawText(rect2, Qt::AlignCenter, QString(QString::number(m_nodeNumber[1])));

    QRect rect3(-(m_polygonWidth / 2 + RETANGULAR_WIDTH / 2 + TEXT_WIDTH), (m_polygonHeight / 2 - TEXT_WIDTH / 2), TEXT_WIDTH, TEXT_WIDTH);
    painter.drawText(rect3, Qt::AlignCenter, QString(QString::number(m_nodeNumber[2])));

    painter.restore();

    DrawTail(painter);
}

void IndShimming::DrawFourNodes(QPainter &painter)
{
    QPoint topLeft(width() / 2 - m_polygonWidth / 2 - RETANGULAR_WIDTH / 2,
                   height() / 2 - m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2);
    QPoint topRight(width() / 2 + m_polygonWidth / 2 - RETANGULAR_WIDTH / 2,
                    height() / 2 - m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2);
    QPoint bottomRight(width() / 2 + m_polygonWidth / 2 - RETANGULAR_WIDTH / 2,
                       height() / 2 + m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2);
    QPoint bottomLeft(width() / 2 - m_polygonWidth / 2 - RETANGULAR_WIDTH / 2,
                      height() / 2 + m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2);

    if (m_items.size() == 4)
    {
        m_items[m_nodeNumber[0]]->move(topLeft);
        m_items[m_nodeNumber[1]]->move(topRight);
        m_items[m_nodeNumber[2]]->move(bottomRight);
        m_items[m_nodeNumber[3]]->move(bottomLeft);
    }

    if (m_lineStyle != LineStyle::HIDE)
    {
        DrawLine(painter, QPoint(-(m_polygonWidth / 2 - RETANGULAR_WIDTH / 2), -m_polygonHeight / 2), QPoint(m_polygonWidth / 2 - RETANGULAR_WIDTH / 2, -m_polygonHeight / 2));
        DrawLine(painter, QPoint(m_polygonWidth / 2, -(m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2)), QPoint(m_polygonWidth / 2, m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2));
        DrawLine(painter, QPoint(m_polygonWidth / 2 - RETANGULAR_WIDTH / 2, m_polygonHeight / 2), QPoint(-(m_polygonWidth / 2 - RETANGULAR_WIDTH / 2), m_polygonHeight / 2));
    }

    painter.save();

    QFont font;
    font.setFamilies({"Noto Sans", "sans-serif"});
    font.setPixelSize(22);
    painter.setFont(font);
    painter.setPen(QColor("#003868"));

    QRect rect1(-(m_polygonWidth / 2 + RETANGULAR_WIDTH / 2 + TEXT_WIDTH), -(m_polygonHeight / 2 + TEXT_WIDTH / 2), TEXT_WIDTH, TEXT_WIDTH);
    painter.drawText(rect1, Qt::AlignCenter, QString(QString::number(m_nodeNumber[0])));

    QRect rect2((m_polygonWidth / 2 + RETANGULAR_WIDTH / 2), -(m_polygonHeight / 2 + TEXT_WIDTH / 2), TEXT_WIDTH, TEXT_WIDTH);
    painter.drawText(rect2, Qt::AlignCenter, QString(QString::number(m_nodeNumber[1])));

    QRect rect3((m_polygonWidth / 2 + RETANGULAR_WIDTH / 2), (m_polygonHeight / 2 - TEXT_WIDTH / 2), TEXT_WIDTH, TEXT_WIDTH);
    painter.drawText(rect3, Qt::AlignCenter, QString(QString::number(m_nodeNumber[2])));

    QRect rect4(-(m_polygonWidth / 2 + RETANGULAR_WIDTH / 2 + TEXT_WIDTH), (m_polygonHeight / 2 - TEXT_WIDTH / 2), TEXT_WIDTH, TEXT_WIDTH);
    painter.drawText(rect4, Qt::AlignCenter, QString(QString::number(m_nodeNumber[3])));

    painter.restore();

    DrawTail(painter);
}

void IndShimming::DrawLine(QPainter& painter, const QPointF& start, const QPointF& end)
{
    painter.save();

    QPen pen(QColor(0xff003868));
    pen.setWidthF(1.5);
    if (m_lineStyle == LineStyle::DASH)
    {
        pen.setStyle(Qt::DashLine);
    }
    painter.setPen(pen);
    painter.drawLine(start, end);

    painter.restore();
}

void IndShimming::DrawHR()
{
    if (m_HR == nullptr)
    {
        m_HR = new QLabel(this);
        m_HR->setStyleSheet(
            "background-color: #003868;"
            "color: white;"
            "font-family: 'Noto Sans', sans-serif;"
            "font-size: 14px;"
            "padding: 5px;"
            "border: none;"
            "text-align: center;"
            );
        m_HR->setFixedSize(TEXT_WIDTH, TEXT_WIDTH);
        m_HR->setAlignment(Qt::AlignCenter);
        m_HR->setText("HR");
        if (m_items.size() == 4)
        {
            m_HR->move(width() / 2 - m_polygonWidth / 2 - TEXT_WIDTH / 2,
                       height() / 2 - m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2 - TEXT_WIDTH);
        }
        else if (m_items.size() == 3)
        {
            m_HR->move(width() / 2 - TEXT_WIDTH / 2,
                       height() / 2 - m_polygonHeight / 2 - RETANGULAR_HEIGHT / 2 - TEXT_WIDTH);
        }
        m_HR->show();
    }
}

void IndShimming::DrawTail(QPainter& painter)
{
    painter.save();

    painter.setPen(Qt::NoPen);
    painter.setBrush(QColor(0xff009cdd));

    QPolygon polygon;
    polygon.setPoints(7, -(m_polygonWidth / 2 + TEXT_WIDTH / 4), m_polygonHeight / 2 + RETANGULAR_HEIGHT / 2,
                      -(m_polygonWidth / 2 - TEXT_WIDTH / 4), m_polygonHeight / 2 + RETANGULAR_HEIGHT / 2,
                      -(m_polygonWidth / 2 - TEXT_WIDTH / 4), m_polygonHeight / 2 + RETANGULAR_HEIGHT / 2 + 16,
                      -(m_polygonWidth / 2 - TEXT_WIDTH / 2), m_polygonHeight / 2 + RETANGULAR_HEIGHT / 2 + 16,
                      -m_polygonWidth / 2, m_polygonHeight / 2 + RETANGULAR_HEIGHT / 2 + TEXT_WIDTH,
                      -(m_polygonWidth / 2 + TEXT_WIDTH / 2), m_polygonHeight / 2 + RETANGULAR_HEIGHT / 2 + 16,
                      -(m_polygonWidth / 2 + TEXT_WIDTH / 4), m_polygonHeight / 2 + RETANGULAR_HEIGHT / 2 + 16);
    painter.drawPolygon(polygon);

    painter.restore();
}

void IndShimming::SetCOGLocation(double x, double y)
{
    m_COG_x = x;
    m_COG_y = y;
    m_cog->move(width() / 2 - COG_DIAMETER_RADIUS + x, height() / 2 - COG_DIAMETER_RADIUS - y);
}

void IndShimming::SetNodesNumber(const std::vector<char>& nodes)
{
    if (nodes.size() > 8)
    {
        return;
    }
    for (auto& itr : m_items) {
        delete itr.second;
    }
    for (char i = 0; i < MAX_CELL_COUNTS; i++) {
        m_nodeNumber[i] = 0;
    }
    m_items.clear();
    for (char i = 0; i < nodes.size(); i++) {
        if (nodes[i] != 0)
        {
            IndShimmingItem* item = new IndShimmingItem(this);
            item->SetNodeNumber(nodes[i]);
            item->setFixedSize(RETANGULAR_WIDTH, RETANGULAR_HEIGHT);
            m_items[nodes[i]] = item;
            m_nodeNumber[i] = nodes[i];
        }
    }
    if (m_polygonType == PolygonType::NONE_REGULAR && nodes.size() == 3) {
        int temp = m_polygonWidth;
        m_polygonWidth = m_polygonHeight;
        m_polygonHeight = temp;
    }
    update();
}

void IndShimming::SetLineStyle(LineStyle style)
{
    m_lineStyle = style;
    update();
}

void IndShimming::SetWidthHeightRatio(double ratio)
{
    m_widthHeightRatio = ratio;
    m_polygonWidth *= m_amplificationFactor;
    m_polygonHeight = m_polygonWidth * (ratio * m_amplificationFactor);
    update();
}

void IndShimming::SetCellCounts(const std::vector<int>& counts)
{
    m_cellCounts = counts;
    int total = 0;
    for (char i = 0; i < counts.size(); ++i) {
        auto itr = m_items.find(m_nodeNumber[i]);
        if (itr != m_items.end()) {
            itr->second->SetCount(counts[i]);
            total += counts[i];
        }
    }

    int totalRatio = 0;
    for (char i = 0; i < counts.size(); ++i) {
        auto itr = m_items.find(m_nodeNumber[i]);
        if (itr != m_items.end()) {
            double ratio = (double)counts[i] / total * 100;
            if (i == counts.size() - 1) {
                itr->second->SetRatio(100 - totalRatio);
            } else {
                itr->second->SetRatio(ratio);
            }
            totalRatio += std::floor(ratio);
        }
    }
}

void IndShimming::SetPolygonType(PolygonType type)
{
    m_polygonType = type;
    if (m_polygonType == PolygonType::REGULAR) {
        m_polygonWidth = REGULAR_DEFAULT_WIDTH * m_amplificationFactor;
        m_polygonHeight = m_polygonWidth * (m_amplificationFactor * m_widthHeightRatio);
    } else {
        m_widthHeightRatio = DEFAULT_NO_REGULAR_RATIO;
        m_polygonWidth = NONE_REGULAR_WIDTH * m_amplificationFactor;
        m_polygonHeight = m_polygonWidth * (m_amplificationFactor * m_widthHeightRatio);
        if (m_polygonType == PolygonType::NONE_REGULAR && m_items.size() == 3) {
            int temp = m_polygonWidth;
            m_polygonWidth = m_polygonHeight;
            m_polygonHeight = temp;
        }
    }
    update();
}

void IndShimming::SetAmplificationFactor(double factor)
{
    if (factor <= 1)
    {
        return;
    }
    m_amplificationFactor = factor;
    m_polygonWidth *= factor;
    m_polygonHeight = m_polygonWidth * (m_widthHeightRatio * factor);
    update();
}

void IndShimming::ShowBlueTriangle(int nodeNumber)
{
    auto itr = m_items.find(nodeNumber);
    if (itr != m_items.end()) {
        itr->second->SetShowTriangle(true);
    }
}

void IndShimming::ClearBlueTriangle(int nodeNumber)
{
    auto itr = m_items.find(nodeNumber);
    if (itr != m_items.end()) {
        itr->second->SetShowTriangle(false);
    }
}

void IndShimming::SetHighlight(int nodeNumber)
{
    auto itr = m_items.find(nodeNumber);
    if (itr != m_items.end()) {
        itr->second->SetHighlight(true);
    }
}

void IndShimming::ClearHighLight(int nodeNumber)
{
    auto itr = m_items.find(nodeNumber);
    if (itr != m_items.end()) {
        itr->second->SetHighlight(false);
    }
}

void IndShimming::SetLoadType(LoadType type)
{
    m_loadType = type;
    IndShimmingItem::m_loadType = type;
    update();
}
